<?php

class Storage_Adapter_Ftp extends Storage_Adapter
{
	/**
	 * @var resource
	 */
	protected $_stream = NULL;

	/**
	 * @var string
	 */
	protected $_host = 'localhost';

	/**
	 * @var int
	 */
	protected $_port = 21;

	/**
	 * @var string
	 */
	protected $_username = '';

	/**
	 * @var string
	 */
	protected $_password = '';

	/**
	 * @var string
	 */
	protected $_timeout = 100;

	/**
	 * @var bool
	 */
	protected $_ssl = FALSE;

	function __construct($id, $config)
	{
		$this -> _host = $config['host'];
		$this -> _port = $config['port'];
		$this -> _username = $config['username'];
		$this -> _password = $config['password'];
		$this -> _ssl = $config['ssl'];
		$this -> _basePath = rtrim($config['basePath'], '/') . '/';
		$this -> _baseUrl = rtrim($config['baseUrl'], '/') . '/';
	}

	/**
	 * connect ftp resource handle
	 * @return void
	 */
	function connect()
	{
		if ($this -> _stream and is_resource($this -> _stream))
		{
			return;
		}

		if ($this -> _ssl)
		{
			$this -> _stream = @ftp_ssl_connect($this -> _host, $this -> _port, $this -> _timeout);
		}
		else
		{
			$this -> _stream = @ftp_connect($this -> _host, $this -> _port, $this -> _timeout);

		}

		if (NULL == $this -> _stream)
		{
			throw new Storage_Exception('could not connect to {host}:{port}', array(
				'{host}' => $this -> _host,
				'{port}' => $this -> _port
			));
		}

		if (!@ftp_login($this -> _stream, $this -> _username, $this -> _password))
		{
			throw new Storage_Exception('could not authenticate to {host}:{port}', array(
				'{host}' => $this -> _host,
				'{port}' => $this -> _port,
			));
		}
	}

	/**
	 * disconnect session
	 * @return void
	 */
	function disconnect()
	{
		if ($this -> _stream and is_resource($this -> _stream))
		{
			@ftp_close($this -> _stream);
			$this -> _stream = NULL;
		}
	}

	/**
	 * @return resource
	 */
	function getStream()
	{
		$this -> _stream or $this -> connect();

		return $this -> _stream;
	}

	/**
	 * @param string $command
	 * @param bool $force optional default TRUE
	 * @return TRUE|FALSE
	 * @throws Storage_Exception
	 */
	public function command($command, $force = TRUE)
	{
		if (!@ftp_site($this -> getStream(), $command))
		{
			if ($force)
			{
				throw new Storage_Exception('Unable to execute SITE {cmd}', array('{cmd}' => $command));
			}
			return FALSE;
		}
		return TRUE;
	}

	/**
	 * @param string $path relative path
	 * @param string $local_file  absolute local file path
	 * @return true|false
	 * @throws Storage_Exception
	 */
	function put($relative_path, $local_file, $force = TRUE)
	{

		if (!is_file($local_file))
		{
			if ($force)
			{
				throw new Storage_Exception('file does not exsits {file}', array('{file}' => $local_file, ));
			}
			return FALSE;
		}

		// try to make dir to resolve issue with no directory exists.
		if ($this -> makeDir(dirname($relative_path)))
		{
			// try to make dir before put data or content.
		}

		$path = $this -> mapPath($relative_path);

		if (!@ftp_put($this -> getStream(), $path, $local_file, FTP_ASCII))
		{
			if ($force)
			{
				throw new Storage_Exception('could not put "{from}" to {to}', array(
					'{from}' => $local_file,
					'{to}' => $path,
				));
			}
		}
		return TRUE;
	}

	/**
	 * copy content of relative path to local file and set mode
	 * @param string $local_file absolute file path
	 * @param string $relative_path
	 * @param int mode
	 * @return TRUE|FALSE
	 * @throws Storage_Exception
	 */
	public function get($relative_path, $local_file, $force = TRUE)
	{
		self::_makeLocalDir(dirname($local_file));

		$path = $this -> mapPath($relative_path);

		// Get mode
		if (null == $mode)
		{
			$mode = $this -> getMode($path);
		}

		$resource = $this -> getStream();

		// Non-blocking mode
		if (@function_exists('ftp_nb_get'))
		{
			$res = @ftp_nb_get($resource, $local_file, $path, FTP_BINARY);
			while ($res == FTP_MOREDATA)
			{
				$res = @ftp_nb_continue($resource);
			}
			$return = ($res === FTP_FINISHED);
		}
		// Blocking mode
		else
		{
			$return = @ftp_get($resource, $local_file, $path, FTP_BINARY);
		}

		// Error
		if ($return === FALSE)
		{
			if ($force)
			{
				throw new Storage_Exception('Unable to get "{from}" to "{to}"', array(
					'{from}' => $path,
					'{to}' => $local_file
				));
			}
			return FALSE;
		}

		return TRUE;
	}

	function putContents($relative_path, $data, $force = TRUE)
	{

	}

	function getContents($relative_path, $force = TRUE)
	{

	}

	/**
	 * @link http://www.php.net/manual/en/function.ssh2-sftp-unlink.php
	 * delete relative path
	 * @param $force  optional = FALSE
	 * @param string $relative_path
	 */
	function delete($relative_path, $force = FALSE)
	{
		$path = $this -> getPath($relative_path);

		if (!@ftp_delete($this -> getStream(), $path))
		{
			if ($force)
			{
				throw new Storage_Exception('could not remove {from}', array('{from}' => $path, ));
			}
			return FALSE;
		}
		return TRUE;
	}

	public function isFile($relative_path)
	{
		return @ftp_mdtm($ftp_stream, $this -> mapPath($relative_path));
	}

	/**
	 * @param string $relative_path
	 */
	public function isDir($relative_path)
	{
		$path = $this -> mapPath($relative_path);

		$ftp = $this -> getStream();

		if (!$pwd = @ftp_pwd($ftp))
		{
			$pwd = '/';
		}

		$isDir = @ftp_chdir($ftp, $path);

		if ($isDir)
		{
			@ftp_chdir($ftp, $pwd);
		}

		return $isDir;
	}

	/**
	 * @link http://www.php.net/manual/en/function.ssh2-sftp-mkdir.php
	 * @param string $relative_path
	 * @param $mode
	 * @return TRUE|FALSE
	 */
	function makeDir($relative_path, $mode = 0755)
	{

		// Check if the directory already exists
		if ($this -> isDir($relative_path))
		{
			return TRUE;
		}

		$ftp_stream = $this -> getStream();

		$path = $this -> mapPath($relative_path);

		$parts = array_filter(explode('/', $path));

		$pPath = array_shift($parts);

		while (count($parts) > 0)
		{
			$pPath .= '/' . array_shift($parts);

			// If it doesn't exist, create it
			if (!@ftp_chdir($ftp_stream, $pPath))
			{
				if (!@ftp_mkdir($ftp_stream, $pPath))
				{
					throw new Storage_Exception('Unable to make directory "{dir}"', array('dir' => $pPath));
				}
			}
		}

		if (!@ftp_chdir($ftp_stream, $path))
		{
			throw new Storage_Exception('Unable to make directory "{dir}"', array('dir' => $path));
		}
		return TRUE;
	}

	/**
	 * @link http://www.php.net/manual/en/function.ssh2-sftp-rmdir.php
	 * @param string $relative_path
	 * @param bool $force optional FALSE
	 * @return bool
	 * @throws Storage_Exception
	 */
	function removeDir($relative_path, $force = FALSE)
	{
		$path = $this -> mapPath($relative_path);

		if (!@ftp_rmdir($this -> getStream(), $path))
		{
			if ($force)
			{
				throw new Storage_Exception('could not remove "{dir}"', array('{dir}' => $path));
			}
			return FALSE;
		}
		return TRUE;
	}

}
